using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Udp
{
    /// <summary>
    /// Udp通信类
    /// </summary>
    public class NetUdpClient : ClientAbs
    {
        private readonly static ITimestampFactory _sendDataIntervalTimestampFactory;
        static NetUdpClient()
        {
            _sendDataIntervalTimestampFactory = new TicksTimestampFactory(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local), TimestampUnit.Nanosecond100);
        }


        private readonly NetUdpClientInitPara _udpClientInitPara;
        /// <summary>
        /// 获取udp初始化参数
        /// </summary>
        public NetUdpClientInitPara UdpClientInitPara
        {
            get { return this._udpClientInitPara; }
        }


        private readonly object _sendDataLock = new object();
        /// <summary>
        /// 接收Socket
        /// </summary>
        private Socket _client = null;
        /// <summary>
        /// 接收Socket
        /// </summary>
        public Socket Client
        {
            get { return this._client; }
        }

        /// <summary>
        /// 获取指示当前客户端与服务端口的连接状态
        /// </summary>
        public override bool Connected
        {
            get
            {
                return this._client != null;
            }
        }

        private readonly byte[] _buffer;
        private readonly BinaryReader _reader;
        private readonly NetLinkInfo _netLinkInfo;
        private readonly NetUdpUnpackerPara _unpackerPara;
        private SocketAsyncEventArgs _recvEventArg = null;

        private bool _running = false;
        /// <summary>
        /// true:运行;false:停止
        /// </summary>
        public bool Status
        {
            get { return _running; }
        }

        private bool _asyncRunning = false;
        private readonly object _runningLock = new object();
        /// <summary>
        /// udp启动完成通知事件
        /// </summary>
        public event EventHandler<UdpStartedCompletedArgs> UdpStartedCompleted;
        private void OnRaiseUdpStartedCompleted(bool succes)
        {
            var handler = this.UdpStartedCompleted;
            if (handler != null)
            {
                handler(this, new UdpStartedCompletedArgs(succes));
            }
        }



        /// <summary>
        /// 接收线程
        /// </summary>
        private readonly ThreadEx _receiveThread = null;

        private readonly object _sendIntervalLock = new object();
        private readonly long _sendInterval100NS = 0;
        private long _lastSendDataTimestamp100NS = 0;


        private readonly bool _enableSendHeartData = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="initPara">Udp客户端初始化参数</param>
        /// <exception cref="ArgumentNullException">dataOutput或receiveEndPoint为null将抛出异常</exception>
        public NetUdpClient(NetUdpClientInitPara initPara)
            : base(initPara)
        {
            if (initPara.ClientHeart != null)
            {
                ClientHeartManager.Instance.Add(this, initPara.ClientHeart);
                this._enableSendHeartData = true;
            }

            this._udpClientInitPara = initPara;
            this._buffer = new byte[initPara.ReceiveBufferSize];
            this._reader = new BinaryReader(new MemoryStream(this._buffer));
            this._netLinkInfo = new NetLinkInfo(null, initPara.LocalEndPoint);

            if (initPara.ReceiveData)
            {
                if (initPara.Unpacker == null)
                {
                    throw new ArgumentException("分包器未指定");
                }

                this._unpackerPara = new NetUdpUnpackerPara()
                {
                    Client = this,
                    LinkInfo = this._netLinkInfo,
                    Buffer = this._buffer,
                    Offset = 0,
                    Reader = this._reader
                };

                switch (initPara.ReceiveDataMode)
                {
                    case ThreadMode.Sync:
                    case ThreadMode.Spin:
                        string receiveThreadName = initPara.ReceiveThreadName;
                        if (string.IsNullOrWhiteSpace(receiveThreadName))
                        {
                            receiveThreadName = $"udp接收数据线程{NetHelper.GetIPPortString(initPara.LocalEndPoint)}";
                        }
                        this._receiveThread = new ThreadEx(this.ReceiveDataThreadMethod, receiveThreadName, true);
                        break;
                    case ThreadMode.Async:
                        break;
                    default:
                        //不接收数据
                        break;
                }
            }

            this._sendInterval100NS = initPara.SendInterval100NS;
        }


        private int _asyncReceiveThreadReceiveCount = 0;
        /// <summary>
        /// tcp客户端开始接受tcp服务器发送的数据
        /// </summary>
        private void StartRecv(SocketAsyncEventArgs args)
        {
            if (this._disposed || !this._running)
            {
                return;
            }

            this._asyncReceiveThreadReceiveCount++;
            if (this._asyncReceiveThreadReceiveCount > this._udpClientInitPara.AsyncReceiveStackDepth)
            {
                this._asyncReceiveThreadReceiveCount = 0;
                Task.Factory.StartNew(() =>
                {
                    this.PrimitiveStartRecv(args);
                });
            }
            else
            {
                this.PrimitiveStartRecv(args);
            }
        }

#if !NET4_0
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        private void PrimitiveStartRecv(SocketAsyncEventArgs args)
        {
            //bool willRaiseEvent = args.AcceptSocket.ReceiveAsync(args);
            var socket = args.AcceptSocket;
            if (socket == null)
            {
                return;
            }

            bool willRaiseEvent = socket.ReceiveFromAsync(args);
            //bool willRaiseEvent = args.AcceptSocket.ReceiveMessageFromAsync(args);
            if (!willRaiseEvent)
            {
                this.ProcessReceive(args);
            }
        }


        private void RecvEventArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.ReceiveFrom:
                        this.ProcessReceive(e);
                        break;
                    case SocketAsyncOperation.Send:
                        //ProcessSend(e);
                        break;
                    default:
                        throw new ArgumentException("The last operation completed on the socket was not a receive or send");
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }


        /// <summary>
        /// 处理接受到的tcp服务器数据
        /// </summary>
        /// <param name="e"></param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                if (!this._running)
                {
                    return;
                }

                if (e.SocketError == SocketError.Success)
                {
                    if (e.BytesTransferred > 0)
                    {
                        this.OutputReceiveDataBuffer(e.RemoteEndPoint, 0, e.BytesTransferred);
                    }
                }
                else
                {
                    ZLoger.Error(e.ConnectByNameError, $"socket udp接收数据错误，{e.SocketError.ToString()}");
                }

                this.StartRecv(e);
            }
            catch (ObjectDisposedException)
            {

            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        private void OutputReceiveDataBuffer(EndPoint remoteEndPoint, int offset, int revLen)
        {
            base.UpdateLastReceiveDataTimestamp();
            this._unpackerPara.DataLength = revLen;
            this._unpackerPara.ReceiveLength = revLen;
            this._unpackerPara.Offset = offset;
            this._unpackerPara.Reader.BaseStream.Position = offset;
            this._netLinkInfo.RemoteEndPoint = remoteEndPoint;
            base._initPara.Unpacker.UnPackge(this._unpackerPara);
        }


        /// <summary>
        /// udp接收数据线程方法
        /// </summary>
        /// <param name="threadPara">线程参数</param>
        private void ReceiveDataThreadMethod(ThreadPara threadPara)
        {
            try
            {
                ZLoger.Info($"UDP同步接收数据线程\"{Thread.CurrentThread.Name}\"已启动...");
                this._running = true;
                var client = (Socket)threadPara.Para;
                EndPoint remoteEP = new IPEndPoint(1, 1);
                int revLen = 0;
                bool isSpin = this._udpClientInitPara.ReceiveDataMode == ThreadMode.Spin;

                while (this._running)
                {
                    try
                    {
                        if (base._disposed || threadPara.Token.IsCancellationRequested)
                        {
                            break;
                        }

                        if (isSpin)
                        {
                            while (this._running)
                            {
                                if (client.Available > 0)
                                {
                                    revLen = client.ReceiveFrom(this._buffer, ref remoteEP);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            //revLen = client.ReceiveFrom(buffer, offset, size, SocketFlags.None, ref remoteEP);//注：使用此重载方法接收数据会存在数据被截断bug
                            revLen = client.ReceiveFrom(this._buffer, ref remoteEP);
                        }

                        if (revLen <= 0 || !this._running)
                        {
                            continue;
                        }

                        this.OutputReceiveDataBuffer(remoteEP, 0, revLen);
                    }
                    catch (ThreadAbortException)
                    {
                        break;
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }
                    catch (SocketException sex)
                    {
                        if (base._disposed || threadPara.Token.IsCancellationRequested || this._running)
                        {
                            break;
                        }
                        else if (!NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                        {
                            ZLoger.Error(sex, "udp接收数据发生异常");
                        }
                    }
                    catch (Exception exi)
                    {
                        if (base._disposed || threadPara.Token.IsCancellationRequested || this._running)
                        {
                            break;
                        }
                        else
                        {
                            ZLoger.Error(exi, "udp接收数据发生异常");
                        }
                    }
                }

                ZLoger.Info($"UDP同步接收数据线程\"{Thread.CurrentThread.Name}\"已停止...");
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }




        private void QuitMulticast(Socket client)
        {
            if (this._udpClientInitPara.Transfer == UDPTransferMode.Multicast)
            {
                // 离开组播组
                client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DropMembership, new MulticastOption(this._udpClientInitPara.MulticastIPAddress));
            }
        }

        private Socket CreateSocket()
        {
            var initPara = this._udpClientInitPara;
            Socket client = null;
            switch (initPara.InterNetwork)
            {
                case InterNetworkType.InterNetworkV4:
                    client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    break;
                case InterNetworkType.InterNetworkV6:
                    client = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
                    break;
                case InterNetworkType.All:
                default:
#if NET4_0
                    client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
#else
                    client = new Socket(SocketType.Dgram, ProtocolType.Udp);
#endif
                    break;
            }

            client.ReceiveBufferSize = initPara.ReceiveBufferSize;
            client.SendBufferSize = initPara.SendBufferSize;

            switch (initPara.Transfer)
            {
                case UDPTransferMode.Unicast:
                    break;
                case UDPTransferMode.Multicast:
                    if (initPara.Multicast == MulticastMode.Send)
                    {
                        // 加入组播组
                        client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(initPara.MulticastIPAddress));
                        //client.Ttl = 1;
                        client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, initPara.Ttl);//// 设置TTL（Time To Live）
                    }
                    else
                    {
                        client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(initPara.MulticastIPAddress, ((IPEndPoint)initPara.LocalEndPoint).Address));
                    }
                    break;
                case UDPTransferMode.Broadcast:
                    //client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                    client.EnableBroadcast = true;
                    break;
            }


            //如果没有这两行代码,则windows底层BUG会在接收抛出异常,并将socket重置,然后再也收不到数据
            const int SIO_UDP_CONNRESET = -1744830452;
            client.IOControl(SIO_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null);

            if (initPara.SendMillisecondsTimeout > 0)
            {
                client.SendTimeout = initPara.SendMillisecondsTimeout;
            }

            if (initPara.ReceiveMillisecondsTimeout > 0)
            {
                client.ReceiveTimeout = initPara.ReceiveMillisecondsTimeout;
            }

            return client;
        }
        private void StartReceive(Socket socket)
        {
            if (!this._udpClientInitPara.ReceiveData)
            {
                return;
            }

            switch (this._udpClientInitPara.ReceiveDataMode)
            {
                case ThreadMode.Sync:
                case ThreadMode.Spin:
                    if (this._receiveThread != null)
                    {
                        this._receiveThread.Start(socket);
                    }
                    break;
                case ThreadMode.Async:
                    if (this._recvEventArg != null)
                    {
                        this._recvEventArg.AcceptSocket = null;
                        this._recvEventArg.Dispose();
                        this._recvEventArg = null;
                    }

                    var recvEventArgs = new SocketAsyncEventArgs();
                    recvEventArgs.AcceptSocket = socket;
                    recvEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                    recvEventArgs.SetBuffer(this._buffer, 0, this._buffer.Length);
                    recvEventArgs.Completed += this.RecvEventArgs_Completed;
                    this._recvEventArg = recvEventArgs;
                    this.StartRecv(this._recvEventArg);
                    this._running = true;
                    break;                
                default:
                    break;
            }
        }



        /// <summary>
        /// 开始接收数据
        /// </summary>
        public void Start()
        {
            lock (this._runningLock)
            {
                if (this._client != null)
                {
                    return;
                }

                Socket client = this.CreateSocket();
                try
                {
                    client.Bind(this._udpClientInitPara.LocalEndPoint);
                    if (this._udpClientInitPara.RemoteEndPoint != null)
                    {
                        client.Connect(this._udpClientInitPara.RemoteEndPoint);
                    }
                    this._client = client;
                    this.StartReceive(client);
                    this.OnRaiseUdpStartedCompleted(true);
                }
                catch (SocketException sex)
                {
                    this.OnRaiseUdpStartedCompleted(false);
                    this.QuitMulticast(client);
                    client.Dispose();
#if CORE
                    throw new SocketException(sex.ErrorCode, $"{sex.Message},{NetHelper.GetIPPortString(this._udpClientInitPara.LocalEndPoint)}");
#else
                    throw new Exception($"{sex.Message},{NetHelper.GetIPPortString(this._udpClientInitPara.LocalEndPoint)}", sex);
#endif
                }
                catch (Exception ex)
                {
                    this.OnRaiseUdpStartedCompleted(false);
                    this.QuitMulticast(client);
                    client.Dispose();
                    throw new Exception($"{ex.Message},{NetHelper.GetIPPortString(this._udpClientInitPara.LocalEndPoint)}", ex);
                }
            }
        }

        private void AsyncStartMethod()
        {
            try
            {
                var reconnectIntervalMilliseconds = this._udpClientInitPara.ReconnectIntervalMilliseconds;
                Socket client = this.CreateSocket();
                bool hasBind = true;

                while (this._asyncRunning && !base._disposed)
                {
                    try
                    {
                        if (hasBind)
                        {
                            client.Bind(this._udpClientInitPara.LocalEndPoint);
                            hasBind = false;
                        }

                        if (this._udpClientInitPara.RemoteEndPoint != null)
                        {
                            client.Connect(this._udpClientInitPara.RemoteEndPoint);
                        }
                        break;
                    }
                    catch (Exception exi)
                    {
                        if (hasBind)
                        {
                            if (string.IsNullOrWhiteSpace(this._udpClientInitPara.Name))
                            {
                                ZLoger.Warn(exi, $"UDP绑定本地EndPoint\"{NetHelper.GetIPPortString(this._udpClientInitPara.LocalEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            }
                            else
                            {
                                ZLoger.Warn(exi, $"UDP绑定本地EndPoint\"{NetHelper.GetIPPortString(this._udpClientInitPara.LocalEndPoint)}({this._udpClientInitPara.Name})\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            }
                        }
                        else
                        {
                            if (string.IsNullOrWhiteSpace(this._udpClientInitPara.Name))
                            {
                                ZLoger.Warn(exi, $"UDP Connect 远端EndPoint\"{NetHelper.GetIPPortString(this._udpClientInitPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            }
                            else
                            {
                                ZLoger.Warn(exi, $"UDP Connect 远端EndPoint\"{NetHelper.GetIPPortString(this._udpClientInitPara.RemoteEndPoint)}({this._udpClientInitPara.Name})\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                            }
                        }

                        Thread.Sleep(reconnectIntervalMilliseconds);
                    }
                }

                bool succes;
                lock (this._runningLock)
                {
                    if (!base._disposed && this._asyncRunning)
                    {
                        this._client = client;
                        succes = true;
                        this.StartReceive(client);

                    }
                    else
                    {
                        succes = false;
                        this.QuitMulticast(client);
                        client.Dispose();
                    }
                }
                this.OnRaiseUdpStartedCompleted(succes);
            }
            catch (Exception ex)
            {
                ZLoger.Warn(ex, "UDP异步启动失败.");
            }
        }

        /// <summary>
        /// 异步开始接收数据
        /// </summary>
        public void StartAsync()
        {
            lock (this._runningLock)
            {
                if (this._client != null || this._asyncRunning)
                {
                    return;
                }

                this._asyncRunning = true;
                Task.Factory.StartNew(this.AsyncStartMethod);
            }
        }

        /// <summary>
        /// 停止接收数据
        /// </summary>
        public void Stop()
        {
            lock (this._runningLock)
            {
                this._asyncRunning = false;
                this._running = false;

                if (this._udpClientInitPara.ReceiveDataMode == ThreadMode.Sync)
                {
                    if (this._receiveThread != null)
                    {
                        this._receiveThread.Stop();
                    }
                }

                if (this._client != null)
                {
                    this._client.Close();
                    this.QuitMulticast(this._client);
                    this._client.Dispose();
                    this._client = null;
                }
            }
        }







        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bufferData">要发送的数据对象</param>
        /// <param name="options">发送选项</param>
        unsafe protected override void PrimitiveSend(DataSource bufferData, ISendOptions options)
        {
            if (bufferData == null ||
                bufferData.DataType != DataSourceType.File && bufferData.Length == 0)
            {
                return;
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options), $"udp发送数据选项不能为null,必须为\"{nameof(NetUdpSendOptions)}\"或其子类");
            }

            var udpSendOptions = (NetUdpSendOptions)options;

            switch (bufferData.DataType)
            {
                case DataSourceType.Bytes:
                    this.PrimitiveSendTo(bufferData.Bytes, bufferData.Offset, bufferData.Length, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    break;
                case DataSourceType.Stream:
                    this.SendTo(bufferData.Stream, bufferData.LongOffset, bufferData.LongLength, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    break;
                case DataSourceType.File:
                    using (FileStream fs = File.OpenRead(bufferData.FilePath))
                    {
                        bufferData.UpdateFileBufferLength(fs.Length);
                        this.SendTo(fs, bufferData.LongOffset, bufferData.LongLength, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    }
                    break;
#if CORE
                case DataSourceType.Memory:
                    if (bufferData.LongOffset != 0 || bufferData.LongLength != bufferData.MemoryBuffer.Length)
                    {
                        Memory<byte> memory2 = bufferData.MemoryBuffer.Slice(bufferData.Offset, bufferData.Length);
                        this.SendTo(memory2.Span, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    }
                    else
                    {
                        this.SendTo(bufferData.MemoryBuffer.Span, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    }
                    break;
#endif
                case DataSourceType.BinaryReader:
                    this.SendTo(bufferData.Reader.BaseStream, bufferData.LongOffset, bufferData.LongLength, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    break;
                case DataSourceType.DataPtr:
                    using (var ums = new UnmanagedMemoryStream(bufferData.DataPtr, bufferData.LongLength))
                    {
                        this.SendTo(ums, 0, bufferData.LongLength, udpSendOptions.SocketFlags, udpSendOptions.DstEndPoint);
                    }
                    break;
                default:
                    throw new NotImplementedException($"{nameof(NetUdpClient)}发送数据,未实现的数据源类型{bufferData.DataType.ToString()}");
            }

        }


        /// <summary>
        /// 发送数据,返回已发送数据长度
        /// </summary>
        /// <param name="buffer">要发送的数据</param>
        /// <param name="offset">发送数据起始位置</param>
        /// <param name="size">发送数据大小</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="dstEndPoint">目的地EndPoint</param>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <returns>返回已发送数据长度</returns>
        private int PrimitiveSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint dstEndPoint)
        {
            if (this._client == null)
            {
                throw new InvalidOperationException($"未启动\"{this.GetType().FullName}\"对象");
            }

            if (this._sendInterval100NS > 0)
            {
                lock (this._sendIntervalLock)
                {
                    this.WaitSendInterval();
                    if (this._disposed)
                    {
                        throw new ObjectDisposedException(this.ToString());
                    }

                    return this._client.SendTo(buffer, offset, size, socketFlags, dstEndPoint);
                }
            }
            else
            {
                return this._client.SendTo(buffer, offset, size, socketFlags, dstEndPoint);
            }
        }

        /// <summary>
        /// 发送数据,返回已发送数据长度
        /// </summary>
        /// <param name="buffer">要发送的数据</param>
        /// <param name="dstEndPoint">目的地EndPoint</param>
        /// <returns>返回已发送数据长度</returns>
        public int SendTo(byte[] buffer, EndPoint dstEndPoint)
        {
            lock (this._sendDataLock)
            {
                return this.PrimitiveSendTo(buffer, 0, buffer.Length, SocketFlags.None, dstEndPoint);
            }
        }

        /// <summary>
        /// 发送数据,返回已发送数据长度
        /// </summary>
        /// <param name="buffer">要发送的数据</param>
        /// <param name="offset">发送数据起始位置</param>
        /// <param name="size">发送数据大小</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="dstEndPoint">目的地EndPoint</param>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <returns>返回已发送数据长度</returns>
        public int SendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint dstEndPoint)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (dstEndPoint == null)
            {
                throw new ArgumentNullException(nameof(dstEndPoint));
            }

            if (this._disposed)
            {
                throw new ObjectDisposedException(this.ToString());
            }

            int length;
            lock (this._sendDataLock)
            {
                length = this.PrimitiveSendTo(buffer, offset, size, socketFlags, dstEndPoint);
            }
            base.UpdateLastSendDataTimestamp();
            return length;
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="offset">数据中偏移位置,小于等于0表示从0开始</param>
        /// <param name="length">要发送的数据长度,小于等于0表示发送到数据结尾</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="dstEndPoint">目的地EndPoint</param>
        /// <returns>返回已发送数据长度</returns>
        public long SendTo(Stream stream, long offset, long length, SocketFlags socketFlags, EndPoint dstEndPoint)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (dstEndPoint == null)
            {
                throw new ArgumentNullException(nameof(dstEndPoint));
            }

            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset >= stream.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (length < 0)
            {
                length = stream.Length - offset;
            }
            else
            {
                if (length > ArrayEx.MaxLength)
                {
                    throw new ArgumentOutOfRangeException($"udp发送数据长度不能超过{ArrayEx.MaxLength}");
                }

                if (offset + length > stream.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(length));
                }
            }

            byte[] buffer = new byte[this._client.SendBufferSize];
            int sendLength = buffer.Length, tmpLength;
            stream.Seek(offset, SeekOrigin.Begin);
            long sendedLength = 0L;

            lock (this._sendDataLock)
            {
                while (length > 0)
                {
                    if (length < sendLength)
                    {
                        sendLength = (int)length;
                    }

                    stream.Read(buffer, 0, sendLength);
                    tmpLength = this.PrimitiveSendTo(buffer, 0, sendLength, socketFlags, dstEndPoint);
                    sendedLength += tmpLength;
                    length -= tmpLength;
                }
            }

            base.UpdateLastSendDataTimestamp();
            return sendedLength;
        }

#if CORE
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="socketFlags">SocketFlags</param>
        /// <param name="dstEndPoint">目的地EndPoint</param>
        /// <returns>返回已发送数据长度</returns>
        public int SendTo(Span<byte> buffer, SocketFlags socketFlags, EndPoint dstEndPoint)
        {
            if (this._client == null)
            {
                throw new InvalidOperationException($"未启动\"{this.GetType().FullName}\"对象");
            }

            if (dstEndPoint == null)
            {
                throw new ArgumentNullException(nameof(dstEndPoint));
            }

            int length;
            lock (this._sendDataLock)
            {
                if (this._sendInterval100NS > 0)
                {
                    lock (this._sendIntervalLock)
                    {
                        this.WaitSendInterval();
                        if (this._disposed)
                        {
                            throw new ObjectDisposedException(this.ToString());
                        }

                        length = this._client.SendTo(buffer, socketFlags, dstEndPoint);
                    }
                }
                else
                {
                    length = this._client.SendTo(buffer, socketFlags, dstEndPoint);
                }
            }

            base.UpdateLastSendDataTimestamp();
            return length;
        }
#endif




        private SocketAsyncEventArgs _sendEventArg = null;
        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="dstEndPoint"></param>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="size"></param>
        /// <param name="socketFlags"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public void SendToAsync(EndPoint dstEndPoint, byte[] buffer, int offset = 0, int size = -1, SocketFlags socketFlags = SocketFlags.None)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (dstEndPoint == null)
            {
                throw new ArgumentNullException(nameof(dstEndPoint));
            }

            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException("发送起始位置超出buffer范围");
            }

            if (size < 0)
            {
                size = buffer.Length - offset;
            }

            if (offset + size > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("发送数据大小超出buffer范围");
            }

            lock (this._sendDataLock)
            {
                if (this._sendInterval100NS > 0)
                {
                    lock (this._sendIntervalLock)
                    {
                        this.WaitSendInterval();
                        if (this._disposed)
                        {
                            throw new ObjectDisposedException(this.ToString());
                        }

                        this.PrimitiveSendToAsync(dstEndPoint, buffer, offset, size, socketFlags);
                    }
                }
                else
                {
                    this.PrimitiveSendToAsync(dstEndPoint, buffer, offset, size, socketFlags);
                }
            }
        }

        private void PrimitiveSendToAsync(EndPoint dstEndPoint, byte[] buffer, int offset, int size, SocketFlags socketFlags)
        {
            if (this._client == null)
            {
                throw new InvalidOperationException($"未启动\"{this.GetType().FullName}\"对象");
            }

            if (this._sendEventArg == null)
            {
                this._sendEventArg = new SocketAsyncEventArgs();
                this._sendEventArg.Completed += this.SendEventArg_Completed;
            }
            this._sendEventArg.SetBuffer(buffer, offset, size);
            this._sendEventArg.RemoteEndPoint = dstEndPoint;
            this._client.SendToAsync(this._sendEventArg);
        }

        private void SendEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                base.UpdateLastSendDataTimestamp();
            }
            else
            {
                ZLoger.Warn($"通过UDP发送数据到\"{NetHelper.GetIPPortString(e.RemoteEndPoint)}\"失败");
            }
        }



        /// <summary>
        /// 等待发送间隔[返回true,有等待发送间隔;返回false无等待]
        /// </summary>
        /// <returns>返回true,有等待发送间隔;返回false无等待</returns>
        private void WaitSendInterval()
        {
            var mod100NS = _sendDataIntervalTimestampFactory.GetTimestamp() - this._lastSendDataTimestamp100NS;
            if (mod100NS < this._sendInterval100NS)
            {
                //两次之间的发送间隔小于了最小值，则等待差值时间
                SpinWaiter.SpinWait100Nanoseconds(this._sendInterval100NS - mod100NS);
            }
        }



        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (this._enableSendHeartData)
                {
                    ClientHeartManager.Instance.Remove(this);
                }

                lock (this._runningLock)
                {
                    this._asyncRunning = false;
                    this._running = false;

                    if (this._receiveThread != null)
                    {
                        this._receiveThread.Stop();
                        this._receiveThread.Dispose();
                    }

                    if (this._client != null)
                    {
                        this._client.Close();
                        this.QuitMulticast(this._client);
                        this._client.Dispose();
                        this._client = null;
                    }

                    if (this._recvEventArg != null)
                    {
                        this._recvEventArg.Completed -= this.RecvEventArgs_Completed;
                        this._recvEventArg.Dispose();
                    }

                    if (this._sendEventArg != null)
                    {
                        this._sendEventArg.Completed -= this.SendEventArg_Completed;
                        this._sendEventArg.Dispose();
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex, "释放资源异常");
            }
        }
    }


    /// <summary>
    /// udp启动完成通知事件参数
    /// </summary>
    public class UdpStartedCompletedArgs : EventArgs
    {
        /// <summary>
        /// 异步启动成功标识[true;成功;fasle:失败]
        /// </summary>
        public bool Succes { get; private set; }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="succes">异步启动成功标识[true;成功;fasle:失败]</param>
        public UdpStartedCompletedArgs(bool succes)
        {
            this.Succes = succes;
        }
    }
}
